1 /**
2  * Authors: Pedro Tacla Yamada
3  * Date: June 9, 2014
4  * License: Licensed under the MIT license. See LICENSE for more information
5  * Version: 1.0.2
6  */
7 module colorize.colors;
8 
9 import std..string : format;
10 
11 shared bool _useColors;
12 
13 void forceColors() @trusted {
14     synchronized {
15         _useColors = true;
16     }
17 }
18 
19 shared static this() {
20     import std.stdio : stdout;
21 
22     version (unittest)
23         _useColors = true;
24     else {
25         version (Windows) {
26             _useColors = false;
27         } else {
28             import core.sys.posix.unistd : isatty;
29 
30             _useColors = isatty(stdout.fileno) == 1;
31         }
32     }
33 }
34 
35 private template color_type(int offset) {
36     static enum type : int {
37         init_ = 39 + offset,
38 
39         black = 30 + offset,
40         red = 31 + offset,
41         green = 32 + offset,
42         yellow = 33 + offset,
43         blue = 34 + offset,
44         magenta = 35 + offset,
45         cyan = 36 + offset,
46         white = 37 + offset,
47 
48         light_black = 90 + offset,
49         light_red = 91 + offset,
50         light_green = 92 + offset,
51         light_yellow = 93 + offset,
52         light_blue = 94 + offset,
53         light_magenta = 95 + offset,
54         light_cyan = 96 + offset,
55         light_white = 97 + offset
56     }
57 }
58 
59 alias fg = color_type!0.type;
60 alias bg = color_type!10.type;
61 
62 // Text modes
63 static enum mode : int {
64     init_ = 0,
65     bold = 1,
66     underline = 4,
67     blink = 5,
68     swap = 7,
69     hide = 8
70 }
71 
72 /**
73  * Wraps a string around color escape sequences.
74  *
75  * Params:
76  *   str = The string to wrap with colors and modes
77  *   c   = The foreground color (see the fg enum type)
78  *   b   = The background color (see the bg enum type)
79  *   m   = The text mode        (see the mode enum type)
80  * Example:
81  * ---
82  * writeln("This is blue".color(fg.blue));
83  * writeln(
84  *   color("This is red over green blinking", fg.blue, bg.green, mode.blink)
85  * );
86  * ---
87  */
88 string color(const string str, const fg c = fg.init_, const bg b = bg.init_, const mode m = mode
89         .init_) @trusted {
90     if (_useColors) {
91         return format("\033[%d;%d;%dm%s\033[0m", m, c, b, str);
92     }
93 
94     return str;
95 }
96 
97 unittest {
98     string ret;
99 
100     ret = "This is yellow".color(fg.yellow);
101     assert(ret == "\033[33mThis is yellow\033[0m");
102 
103     ret = "This is light green".color(fg.light_green);
104     assert(ret == "\033[92mThis is light green\033[0m");
105 
106     ret = "This is light blue with red background".color(fg.light_blue, bg.red);
107     assert(ret == "\033[0;94;41mThis is light blue with red background\033[0m");
108 
109     ret = "This is red on blue blinking".color(fg.red, bg.blue, mode.blink);
110     assert(ret == "\033[5;31;44mThis is red on blue blinking\033[0m");
111 
112     ret = color("This is magenta", "magenta");
113     assert(ret == "\033[35mThis is magenta\033[0m");
114 }
115 
116 string colorHelper(const string str, const string name) pure {
117     int code;
118 
119     switch (name) {
120     case "init_":
121         code = 39;
122         break;
123 
124     case "black":
125         code = 30;
126         break;
127     case "red":
128         code = 31;
129         break;
130     case "green":
131         code = 32;
132         break;
133     case "yellow":
134         code = 33;
135         break;
136     case "blue":
137         code = 34;
138         break;
139     case "magenta":
140         code = 35;
141         break;
142     case "cyan":
143         code = 36;
144         break;
145     case "white":
146         code = 37;
147         break;
148 
149     case "light_black":
150         code = 90;
151         break;
152     case "light_red":
153         code = 91;
154         break;
155     case "light_green":
156         code = 92;
157         break;
158     case "light_yellow":
159         code = 93;
160         break;
161     case "light_blue":
162         code = 94;
163         break;
164     case "light_magenta":
165         code = 95;
166         break;
167     case "light_cyan":
168         code = 96;
169         break;
170     case "light_white":
171         code = 97;
172         break;
173 
174     case "bg_init":
175         code = 49;
176         break;
177 
178     case "bg_black":
179         code = 40;
180         break;
181     case "bg_red":
182         code = 41;
183         break;
184     case "bg_green":
185         code = 42;
186         break;
187     case "bg_yellow":
188         code = 43;
189         break;
190     case "bg_blue":
191         code = 44;
192         break;
193     case "bg_magenta":
194         code = 45;
195         break;
196     case "bg_cyan":
197         code = 46;
198         break;
199     case "bg_white":
200         code = 47;
201         break;
202 
203     case "bg_light_black":
204         code = 100;
205         break;
206     case "bg_light_red":
207         code = 101;
208         break;
209     case "bg_light_green":
210         code = 102;
211         break;
212     case "bg_light_yellow":
213         code = 103;
214         break;
215     case "bg_light_blue":
216         code = 104;
217         break;
218     case "bg_light_magenta":
219         code = 105;
220         break;
221     case "bg_light_cyan":
222         code = 106;
223         break;
224     case "bg_light_white":
225         code = 107;
226         break;
227 
228     case "mode_init":
229         code = 0;
230         break;
231     case "mode_bold":
232         code = 1;
233         break;
234     case "mode_underline":
235         code = 4;
236         break;
237     case "mode_blink":
238         code = 5;
239         break;
240     case "mode_swap":
241         code = 7;
242         break;
243     case "mode_hide":
244         code = 8;
245         break;
246 
247     default:
248         throw new Exception("Unknown fg color, bg color or mode \"" ~ name ~ "\"");
249     }
250 
251     return format("\033[%dm%s\033[0m", code, str);
252 }
253 
254 string colorHelper(T)(const string str, const T t = T.init_) pure @safe 
255         if (is(T : fg) || is(T : bg) || is(T : mode)) {
256     return format("\033[%dm%s\033[0m", t, str);
257 }
258 
259 alias background = colorHelper!bg;
260 alias foreground = colorHelper!fg;
261 alias style = colorHelper!mode;
262 
263 alias color = background;
264 alias color = foreground;
265 alias color = style;
266 alias color = colorHelper;
267 
268 unittest {
269     string ret;
270 
271     ret = "This is red on blue blinking".foreground(fg.red).background(bg.blue).style(mode.blink);
272 
273     assert(ret == "\033[5m\033[44m\033[31mThis is red on blue blinking\033[0m\033[0m\033[0m");
274 }